package ca.wilkinsonlab.cardioshare;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.stringtree.json.JSONWriter;

import ca.wilkinsonlab.sadi.pellet.PelletQueryTask;
import ca.wilkinsonlab.sadi.tasks.QueryTask;
import ca.wilkinsonlab.sadi.tasks.Task;
import ca.wilkinsonlab.sadi.tasks.TaskManager;

public class CardioSHAREServlet extends HttpServlet
{
	private final static Log log = LogFactory.getLog(CardioSHAREServlet.class);
	
	private final static TaskManager taskManager = TaskManager.getInstance();
	
	private final static JSONWriter jsonWriter = new JSONWriter();
	
	@Override
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
	{
		String query = request.getParameter("query");
		if (query != null) {
			log.debug("query submitted: " + query);
			
			QueryTask queryTask;
			if ( query.matches("(?s).*http://cardioshare.biordf.net/cardioSHARE/COPD\\.owl.*") )
				queryTask = new DummyQueryTask(query);
			else
				queryTask = new PelletQueryTask(query);
			String taskId = taskManager.startTask(queryTask);
			outputTaskId(taskId, response.getWriter());
		} else {
			String taskId = request.getParameter("poll");
			if (taskId != null) {
				Task queryTask = taskManager.getTask(taskId);
				if (queryTask == null) {
					log.warn("attempt to poll non-existant query taskId " + taskId);
					return;
				} else if (queryTask.isFinished()) {
					List<Map<String, String>> results = ((QueryTask)queryTask).getResults();
					Throwable error = queryTask.getError();
					String errorMessage = error == null ? "" : error.toString();
					outputResults(results, errorMessage, response.getWriter());
					taskManager.disposeTask(taskId);
				} else {
					String status = queryTask.getStatus();
					outputStatus(status, response.getWriter());
				}
			} else {
				/* display the query input page...
				 */
				getServletConfig().getServletContext().getRequestDispatcher("/index.jsp").forward(request, response);
			}
		}
	}
	
	@Override
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
	{
		doGet(request, response);
	}
	
	private void outputTaskId(String taskId, PrintWriter out)
	{
		Map<String, Object> jsonMap = new HashMap<String, Object>();
		jsonMap.put("success", "true");
		jsonMap.put("taskId", taskId);
		out.println(jsonWriter.write(jsonMap));
	}
	
	private void outputResults(List<Map<String, String>> results, String error, PrintWriter out)
	{
		List<String> variableNames = new ArrayList<String>();
		List<List<String>> bindings = new ArrayList<List<String>>();
		if (!results.isEmpty()) {
			/* extract the variable names from the results and sort them;
			 * the reason why will be apparent shortly...
			 */
			variableNames.addAll(results.get(0).keySet());
			Collections.sort(variableNames);
			
			/* convert each binding map to an ordered list of values, whose
			 * order matches that of the variable names in the list above...
			 * I'm positive this is unnecessary, since Ext is just going to
			 * redo what I've undone here, but I couldn't figure it out
			 * last night...
			 */
			for (Map<String, String> result: results) {
				List<String> binding = new ArrayList<String>(variableNames.size());
				for (String var: variableNames) {
					binding.add(result.get(var));
				}
				bindings.add(binding);
			}
		}
		
		Map<String, Object> jsonMap = new HashMap<String, Object>();
		jsonMap.put("fields", variableNames);
		jsonMap.put("rows", bindings);
		jsonMap.put("error", error);
		out.println(jsonWriter.write(jsonMap));
	}

	private void outputStatus(String status, PrintWriter out)
	{
		out.println(jsonWriter.write(status));
	}
}
